package com.cheezeworld.entity
{
	
 	import com.cheezeworld.math.AABBox;
 	import com.cheezeworld.math.Vector2D;
 	
 	import flash.display.*;
 	import flash.events.EventDispatcher;
 	import flash.utils.Dictionary;

	 public class Entity extends EventDispatcher
	 { 
	 	public function get scale():Number{ return m_scale; }	
	 	public function set scale( a_value:Number ):void
	 	{
	 		m_scale = a_value;
	 		dispatchEvent( new EntityEvent( EntityEvent.CHANGE_SCALE, a_value ) );
	 	}
	 	
	 	public function get id():String{ return m_id; }
	 	public function get parent():Entity{ return _parent; }
	 	public function set parent( a_value:Entity ) : void 
	 	{ 
	 		if( _parent ) // if parent exists
	 		{
	 			_parent.removeChild( id );
	 		}
	 		
	 		_parent = a_value;
	 		
	 		if( _parent && _parent.getChildByID( m_id ) == null ) // if parent is not null and does not contain this entity
			{
				_parent.addChild(this);
			} 
	 	} 		
	 	
	 	/**
	 	 * NOTES:	The following variables are left open rather than using getter/setters because they are accessed
	 	 * 			often and thus this VASTLY improves performance.
	 	 * 
	 	 * 			newPos and actualPos ( have to use these since we don't do getter/setter )
	 	 * 		 	newPos var should be what you change if you wish to change pos externally.
	 	 */ 		
	 	public var newPos:Vector2D; 
	 	public var actualPos:Vector2D;
	 	public var rotation:Number; 
	 	public var radius:Number; 		
	 	public var bounds:AABBox; 	
	 	
	 	/**
	 	 * ID's are assigned automatically. If it is necessary to force an ID then set it
	 	 * in the parameter file passed through the constructor.
	 	 * 
	 	 */ 		
	 	
	 	public function Entity( a_params:EntityParams )
	 	{	
	 		actualPos = new Vector2D();
	 		newPos = actualPos.copy();
			m_entityMap = new Dictionary();
			m_gameObjects = [];
			
			scale = a_params.scale;
	 		radius = a_params.radius; 
	 		rotation = a_params.rotation;	 		
	 		
	 		bounds = new AABBox( actualPos, radius, radius );
	 		
	 		if( a_params.id == null ){ m_id = ""+m_nextID++; }
	 		else{ m_id = a_params.id; }
			
			parent = a_params.parent;			
	 	} 		
	 	
	 	public function update( a_timePassed:int ):void
	 	{
	 		setInitialValues( a_timePassed );
	 		
	 		updateChildren( a_timePassed );
	 	}
	 	
	 	public function dispose():void
	 	{
	 		newPos = null;
	 		actualPos = null;
	 		bounds = null;
	 		
			for each( var entity:Entity in m_entityMap  )
			{
				delete m_entityMap[ entity.id ];
				m_entityMap[ entity.id ] = null;
				entity.dispose();
			}
			
			m_entityMap = null;
			
			for( var i:int = 0; i < m_gameObjects.length; i++ )
			{
				m_gameObjects[ i ].dispose();
			}
			
			m_gameObjects = null;
			
	 		_parent = null;
	 		
	 		dispatchEvent( new EntityEvent ( EntityEvent.DISPOSE ) );
	 	}
	 	
	 	public function isOverlapping( a_entity:Entity ):Boolean
	 	{
	 		var distSq:Number = actualPos.distanceSqTo( a_entity.actualPos );
	 		var range:Number = radius + a_entity.radius;
	 		if( distSq < range * range )
	 		{
	 			return true;
	 		}
	 		return false;
	 	}
	 	
	 	public function isOverlappingCircle( a_pos:Vector2D, a_radius:Number ) : Boolean
	 	{
	 		var distSq:Number = actualPos.distanceSqTo( a_pos );
	 		var range:Number = radius + a_radius;
	 		if( distSq < range * range )
	 		{
	 			return true;
	 		}
	 		return false;
	 	}
	 	
	 	public function addChild( a_entity:Entity ):void
	 	{	 			
			if( m_entityMap[ a_entity.id ] != null )
			{
				throw new Error( "<Entity> There is an entity with this id already. Ensure ids are unique." );
				return;
			}
			
			m_entityMap[ a_entity.id ] = a_entity;
			a_entity.parent = this;
	 		dispatchEvent( new EntityEvent( EntityEvent.ADD_ENTITY, a_entity ) );
	 	}
	 	
	 	public function removeChild( a_id:String ):void
	 	{
	 		
			if( Entity( m_entityMap[ a_id ] ) == null )
			{
				throw new Error( "<Entity> There was no entity with a matching id." );
				return;
			}
			
			m_entityMap[ a_id ]._parent = null;
			delete m_entityMap[ a_id ];
			m_entityMap[ a_id ] = null;
	 	}
	 	
	 	public function clearChildren( a_classType:Class = null ) : void
	 	{
			for each( var entity:Entity in m_entityMap )
			{
				if( a_classType ){ if( !( entity is a_classType ) ){ continue; } }
				delete m_entityMap[ entity.id ];
				m_entityMap[ entity.id ] = null;
				entity._parent = null;
			}
	 	}
	 	
	 	public function getChildByID( a_id:String ):Entity{ return Entity( m_entityMap[ a_id ] ); }
	 	
	 	public function getChildren() : Array
	 	{
	 		var tempArray:Array = [];
	 		
	 		for each ( var entity:Entity in m_entityMap )
	 		{
 				tempArray.push( entity );
	 		}
	 		
	 		return tempArray;
	 	}
	 	
	 	public function addGameObject( a_gameObject:IGameObject ):void
	 	{
	 		
	 		if( m_gameObjects.indexOf( a_gameObject ) >= 0 )
	 		{
	 			throw new Error( "<Entity> That Game Object already exists!" );
	 		}
	 		
	 		m_gameObjects.push( a_gameObject );
	 	}
	 	
	 	public function removeGameObject( a_object:IGameObject ):void
	 	{
	 		var removalIndex : int = m_gameObjects.indexOf( a_object );
			if( removalIndex >= 0 )
			{
				m_gameObjects.splice( removalIndex, 1 );
			}
	 	}
	 	
	 	override public function toString():String
	 	{
	 		return ( "<"+id+">" );
	 	}
	 	
	  	// -- PRIVATE --
	  	
	  	protected function updateChildren( a_timePassed:Number ):void
	  	{
	  		for each (var entity:Entity in m_entityMap)
	 		{
	 			entity.update( a_timePassed );
	 		}
	 		for( var i:int = 0; i < m_gameObjects.length; i++ )
	 		{
	 			IGameObject( m_gameObjects[ i ] ).update( a_timePassed );
	 		}
	  	}
	  	
	  	protected function setInitialValues( a_timePassed:int ):void
	  	{
	  		_stepSize = a_timePassed * 0.001;
	 		actualPos.x = newPos.x;
	 		actualPos.y = newPos.y;
	 		
	 		// .. Update bounds ...
	 		bounds.center.x = actualPos.x
			bounds.center.y = actualPos.y;
			bounds.left = bounds.center.x - bounds.halfWidth;
			bounds.right = bounds.center.x + bounds.halfWidth;
			bounds.top = bounds.center.y - bounds.halfHeight;
			bounds.bottom = bounds.center.y + bounds.halfHeight;
			bounds.topLeft.x = bounds.left; 
			bounds.topLeft.y = bounds.top;
			bounds.bottomRight.x = bounds.right; 
			bounds.bottomRight.y = bounds.bottom;
			bounds.topRight.x = bounds.right; 
			bounds.topRight.y = bounds.top;
			bounds.bottomLeft.x = bounds.left; 
			bounds.bottomLeft.y = bounds.bottom;
	  	}
	  	
	  	protected var _stepSize:Number;
	  	protected var _parent:Entity;
	  	
	  	private static var m_nextID:int = 0;
	  	private var m_scale:Number;
	  	private var m_id:String;
	  	private var m_entityMap:Dictionary;
	 	private var m_gameObjects:Array;
	 	 
	 	
	 }
}
